package com.example.livedemo.android.util;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.util.Log;
import android.view.View;
import android.view.ViewPropertyAnimator;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;

import com.example.livedemo.frame.LiveHelper;

/**
 * @author wangxy
 * 
 *         item 添加动画从下往上
 */
public class ItemBottomToTopAddAnimator extends SimpleItemAnimator {

	private static final boolean							DEBUG				= false;

	private static TimeInterpolator							sDefaultInterpolator;

	/**
	 * 当执行remove的时候，通常都是一个条目执行删除动画，然后其他条目填补上来 mPendingRemovals 就用来保存我们需要执行删除动画的条目
	 */
	private ArrayList<RecyclerView.ViewHolder>				mPendingRemovals	= new ArrayList<>();

	/**
	 * 当执行add的时候，通常都是一个新的条目执行添加动画(比如透明度从0~1f)，执行完成后和其他条目连接一体 mPendingAdditions
	 * 就用来保存我们需要执行添加动画的条目
	 */
	private ArrayList<RecyclerView.ViewHolder>				mPendingAdditions	= new ArrayList<>();

	private ArrayList<MoveInfo>								mPendingMoves		= new ArrayList<>();

	private ArrayList<ChangeInfo>							mPendingChanges		= new ArrayList<>();

	private ArrayList<ArrayList<RecyclerView.ViewHolder>>	mAdditionsList		= new ArrayList<>();

	private ArrayList<ArrayList<MoveInfo>>					mMovesList			= new ArrayList<>();

	private ArrayList<ArrayList<ChangeInfo>>				mChangesList		= new ArrayList<>();

	/**
	 * 当执行各种动画的时候，会在动画执行之前将动画添加到列表中 动画执行之后再将其从列表中清除 通过判断列表size()是否>0来判断是否正在进行动画
	 */
	private ArrayList<RecyclerView.ViewHolder>				mAddAnimations		= new ArrayList<>();

	private ArrayList<RecyclerView.ViewHolder>				mMoveAnimations		= new ArrayList<>();

	private ArrayList<RecyclerView.ViewHolder>				mRemoveAnimations	= new ArrayList<>();

	private ArrayList<RecyclerView.ViewHolder>				mChangeAnimations	= new ArrayList<>();

	private static class MoveInfo {

		RecyclerView.ViewHolder	holder;

		int						fromX, fromY, toX, toY;

		MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
			this.holder = holder;
			this.fromX = fromX;
			this.fromY = fromY;
			this.toX = toX;
			this.toY = toY;
		}
	}

	private static class ChangeInfo {

		RecyclerView.ViewHolder	oldHolder, newHolder;

		int						fromX, fromY, toX, toY;

		private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
			this.oldHolder = oldHolder;
			this.newHolder = newHolder;
		}

		ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
			this(oldHolder, newHolder);
			this.fromX = fromX;
			this.fromY = fromY;
			this.toX = toX;
			this.toY = toY;
		}

		@NonNull @Override public String toString() {
			return "ChangeInfo{" + "oldHolder=" + oldHolder + ", newHolder=" + newHolder + ", fromX=" + fromX + ", fromY=" + fromY + ", toX=" + toX + ", toY=" + toY + '}';
		}
	}

	/**
	 * 具体执行动画的方法
	 */
	@Override public void runPendingAnimations() {
		LiveHelper.log().e("runPendingAnimations() called");
		// 判断当前是否有延迟执行的动画
		boolean removalsPending = !mPendingRemovals.isEmpty();
		// 当前是否有待执行的条目移动动画
		boolean movesPending = !mPendingMoves.isEmpty();
		boolean changesPending = !mPendingChanges.isEmpty();
		// 通过待执行动画是否为空来判断当前是否需要执行add相关的动画
		boolean additionsPending = !mPendingAdditions.isEmpty();
		if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
			// nothing to animate
			return;
		}
		// First, remove stuff
		for (RecyclerView.ViewHolder holder : mPendingRemovals) {
			animateRemoveImpl(holder);
		}
		mPendingRemovals.clear();
		// 执行条目移动动画
		if (movesPending) {
			final ArrayList<MoveInfo> moves = new ArrayList<>(mPendingMoves);
			mMovesList.add(moves);
			mPendingMoves.clear();
			Runnable mover = () -> {
				for (MoveInfo moveInfo : moves) {
					animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, moveInfo.toX, moveInfo.toY);
				}
				moves.clear();
				mMovesList.remove(moves);
			};
			// 如果这个移动是删除相关的，那么就先等删除动画结束后再执行（这里的动画是alpha由1到0）
			if (removalsPending) {
				View view = moves.get(0).holder.itemView;
				ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
			} else {
				mover.run();
			}
		}
		// Next, change stuff, to run in parallel with move animations
		if (changesPending) {
			final ArrayList<ChangeInfo> changes = new ArrayList<>(mPendingChanges);
			mChangesList.add(changes);
			mPendingChanges.clear();
			Runnable changer = () -> {
				for (ChangeInfo change : changes) {
					animateChangeImpl(change);
				}
				changes.clear();
				mChangesList.remove(changes);
			};
			if (removalsPending) {
				RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
				ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
			} else {
				changer.run();
			}
		}
		// 处理新增动画
		if (additionsPending) {
			final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>(mPendingAdditions);
			mAdditionsList.add(additions);
			mPendingAdditions.clear();
			Runnable adder = () -> {
				for (RecyclerView.ViewHolder holder : additions) {
					animateAddImpl(holder);
				}
				additions.clear();
				mAdditionsList.remove(additions);
			};
			// 如果当前还有诸如删除更新相关的动画，那么就等待其执行完了再执行新增动画
			if (removalsPending || movesPending || changesPending) {
				long removeDuration = removalsPending ? getRemoveDuration() : 0;
				long moveDuration = movesPending ? getMoveDuration() : 0;
				long changeDuration = changesPending ? getChangeDuration() : 0;
				long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
				View view = additions.get(0).itemView;
				ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
			} else {
				adder.run();
			}
		}
	}

	@Override public boolean animateRemove(final RecyclerView.ViewHolder holder) {
		LiveHelper.log().e("animateRemove() called");
		resetAnimation(holder);
		// 删除动画设置为由左向右离开
		mPendingRemovals.add(holder);
		return true;
	}

	private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
		LiveHelper.log().e("animateRemoveImpl() called");
		final View view = holder.itemView;
		final ViewPropertyAnimator animation = view.animate();
		mRemoveAnimations.add(holder);
		animation.setDuration(getRemoveDuration()).translationX(view.getMeasuredWidth()).setListener(new AnimatorListenerAdapter() {

			@Override public void onAnimationStart(Animator animator) {
				dispatchRemoveStarting(holder);
			}

			@Override public void onAnimationEnd(Animator animator) {
				animation.setListener(null);
				view.setTranslationX(0);
				dispatchRemoveFinished(holder);
				mRemoveAnimations.remove(holder);
				dispatchFinishedWhenDone();
			}
		}).start();
	}

	@Override public boolean animateAdd(final RecyclerView.ViewHolder holder) {
		LiveHelper.log().e("animateAdd() called");
		// 清除重置所有的动画动作
		resetAnimation(holder);

		// 新条目由下向上进入,那么先把item放在下面
		holder.itemView.setTranslationY(holder.itemView.getMeasuredHeight());

		// mPendingAdditions:
		// 这个集合用来保存在这个方法中设置为透明度0的条目在runPendingAnimations方法中,会遍历这些条目，并执行对应的动画
		mPendingAdditions.add(holder);
		return true;
	}

	/**
	 * 执行新增动画
	 *
	 * @param holder
	 *            执行动画的条目
	 */
	private void animateAddImpl(final RecyclerView.ViewHolder holder) {
		final View view = holder.itemView;
		final ViewPropertyAnimator animation = view.animate();
		mAddAnimations.add(holder);
		// 新条目由下向上移动
		animation.translationY(0).setDuration(getAddDuration()).setListener(new AnimatorListenerAdapter() {

			@Override public void onAnimationStart(Animator animator) {
				dispatchAddStarting(holder);
			}

			@Override public void onAnimationCancel(Animator animator) {
				view.setTranslationY(0);
			}

			@Override public void onAnimationEnd(Animator animator) {
				animation.setListener(null);
				dispatchAddFinished(holder);
				mAddAnimations.remove(holder);
				dispatchFinishedWhenDone();
			}
		}).start();
	}

	@Override public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
		LiveHelper.log().e("animateMove() called holder pos： " + holder.getAdapterPosition());
		final View view = holder.itemView;
		fromX += (int) holder.itemView.getTranslationX();
		fromY += (int) holder.itemView.getTranslationY();
		resetAnimation(holder);
		int deltaX = toX - fromX;
		int deltaY = toY - fromY;
		if (deltaX == 0 && deltaY == 0) {
			dispatchMoveFinished(holder);
			return false;
		}
		if (deltaX != 0) {
			view.setTranslationX(-deltaX);
		}
		if (deltaY != 0) {
			view.setTranslationY(-deltaY);
		}
		mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
		return true;
	}

	private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
		final View view = holder.itemView;
		final int deltaX = toX - fromX;
		final int deltaY = toY - fromY;
		if (deltaX != 0) {
			view.animate().translationX(0);
		}
		if (deltaY != 0) {
			view.animate().translationY(0);
		}
		// TODO: make EndActions end listeners instead, since end actions aren't called
		// when
		// vpas are canceled (and can't end them. why?)
		// need listener functionality in VPACompat for this. Ick.
		final ViewPropertyAnimator animation = view.animate();
		mMoveAnimations.add(holder);
		animation.setDuration(getRemoveDuration()).setListener(new AnimatorListenerAdapter() {

			@Override public void onAnimationStart(Animator animator) {
				dispatchMoveStarting(holder);
			}

			@Override public void onAnimationCancel(Animator animator) {
				if (deltaX != 0) {
					view.setTranslationX(0);
				}
				if (deltaY != 0) {
					view.setTranslationY(0);
				}
			}

			@Override public void onAnimationEnd(Animator animator) {
				animation.setListener(null);
				dispatchMoveFinished(holder);
				mMoveAnimations.remove(holder);
				dispatchFinishedWhenDone();
			}
		}).start();
	}

	@Override public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
		if (oldHolder == newHolder) {
			// 当条目复用的时候
			// 当条目执行位置变化动画的时候
			return animateMove(oldHolder, fromX, fromY, toX, toY);
		}
		// 更新条目动画为旧条目由右向左离开 新条目由右向左进入 所以初始状态下旧条目是在原位，而新条目应该在旧条目右边
		// 记录旧条目的位置，透明度
		final float prevTranslationX = oldHolder.itemView.getTranslationX();
		final float prevTranslationY = oldHolder.itemView.getTranslationY();
		final float prevAlpha = oldHolder.itemView.getAlpha();
		resetAnimation(oldHolder);
		int deltaX = (int) (toX - fromX - prevTranslationX);
		int deltaY = (int) (toY - fromY - prevTranslationY);
		// 恢复旧条目的位置透明度（这也是动画结束之后应该恢复的状态）
		oldHolder.itemView.setTranslationX(prevTranslationX);
		oldHolder.itemView.setTranslationY(prevTranslationY);
		oldHolder.itemView.setAlpha(prevAlpha);
		if (newHolder != null) {
			// 设置新条目的初始位置
			resetAnimation(newHolder);
			newHolder.itemView.setTranslationX(-deltaX + oldHolder.itemView.getMeasuredWidth());
			newHolder.itemView.setTranslationY(-deltaY);
			newHolder.itemView.setAlpha(1f);
		}
		mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
		return true;
	}

	private void animateChangeImpl(final ChangeInfo changeInfo) {
		final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
		final View view = holder == null ? null : holder.itemView;
		final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
		final View newView = newHolder != null ? newHolder.itemView : null;
		if (view != null) {
			final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(getChangeDuration());
			mChangeAnimations.add(changeInfo.oldHolder);
			// 旧条目向左移动
			oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX - view.getMeasuredWidth());
			oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
			oldViewAnim.setListener(new AnimatorListenerAdapter() {

				@Override public void onAnimationStart(Animator animator) {
					dispatchChangeStarting(changeInfo.oldHolder, true);
				}

				@Override public void onAnimationEnd(Animator animator) {
					oldViewAnim.setListener(null);
					view.setAlpha(1);
					view.setTranslationX(0);
					view.setTranslationY(0);
					dispatchChangeFinished(changeInfo.oldHolder, true);
					mChangeAnimations.remove(changeInfo.oldHolder);
					dispatchFinishedWhenDone();
				}
			}).start();
		}
		if (newView != null) {
			final ViewPropertyAnimator newViewAnimation = newView.animate();
			mChangeAnimations.add(changeInfo.newHolder);
			newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).alpha(1).setListener(new AnimatorListenerAdapter() {

				@Override public void onAnimationStart(Animator animator) {
					dispatchChangeStarting(changeInfo.newHolder, false);
				}

				@Override public void onAnimationEnd(Animator animator) {
					newViewAnimation.setListener(null);
					newView.setAlpha(1);
					newView.setTranslationX(0);
					newView.setTranslationY(0);
					dispatchChangeFinished(changeInfo.newHolder, false);
					mChangeAnimations.remove(changeInfo.newHolder);
					dispatchFinishedWhenDone();
				}
			}).start();
		}
	}

	/**
	 * 终止change方法中产生的待执行的动画集合中和item相关的条目
	 *
	 * @param infoList
	 * @param item
	 */
	private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {
		for (int i = infoList.size() - 1; i >= 0; i--) {
			ChangeInfo changeInfo = infoList.get(i);
			// endChangeAnimationIfNecessary 方法会判断item是change的新条目还是旧条目
			// 并且将changeInfo中对应的部分设置为null
			if (endChangeAnimationIfNecessary(changeInfo, item)) {
				// 如果changeInfo中新旧条目都是null，那么就可以删除这个动画信息了
				if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
					infoList.remove(changeInfo);
				}
			}
		}
	}

	private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
		if (changeInfo.oldHolder != null) {
			endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
		}
		if (changeInfo.newHolder != null) {
			endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
		}
	}

	/**
	 * 判断当前关闭item先关的change动画是否是必要的
	 *
	 * @param changeInfo
	 *            change的待执行动画信息
	 * @param item
	 *            当前的条目
	 * @return
	 */
	private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
		boolean oldItem = false;
		// 如果这个条目是change中的新条目 那么就直接把这个条目设置为最终形态 并且作为最终的显示如果这个条目是change中的旧条目
		// 那么也要将这个条目是指为最终形态 因为后续的复用中需要使用
		if (changeInfo.newHolder == item) {
			changeInfo.newHolder = null;
		} else if (changeInfo.oldHolder == item) {
			changeInfo.oldHolder = null;
			oldItem = true;
		} else {
			return false;
		}
		item.itemView.setAlpha(1);
		item.itemView.setTranslationX(0);
		item.itemView.setTranslationY(0);
		// 结束change中旧条目的调用
		dispatchChangeFinished(item, oldItem);
		return true;
	}

	@Override public void endAnimation(RecyclerView.ViewHolder item) {
		final View view = item.itemView;
		// 调用cancel的时候，会触发对动画设置的监听
		// 从这里可以推测，对于view的动画，都使用ViewPropertyAnimator补间动画实现，否则调用其cancel无法触发回调;而在view的cancel回调中，均设置其状态为最终完成状态
		view.animate().cancel();
		/**
		 * mPendingMoves 中保存的是需要移动的条目动画 譬如remove动画时本已经上滑但是被强制下滑到原始位置的条目动画
		 * 通常在{@link ItemBottomToTopAddAnimator#runPendingAnimations()} 中执行动画后就会清除
		 * 但是如果动画还没有执行，mPendingMoves 还没有清空，那么就直接将这些需要复原的条目进行复原
		 */
		for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
			MoveInfo moveInfo = mPendingMoves.get(i);
			if (moveInfo.holder == item) {
				view.setTranslationY(0);
				view.setTranslationX(0);
				dispatchMoveFinished(item);
				mPendingMoves.remove(i);
			}
		}
		// 清除change方法中产生的需要执行的动画集合
		endChangeAnimation(mPendingChanges, item);
		// mPendingRemovals这个集合是待执行的删除的条目的集合 删除之后还需要将其状态设置为初始状态
		// 而初始状态和设置的删除动画有关譬如删除动画是将alpha设置为0，那么这里就要还原为1
		if (mPendingRemovals.remove(item)) {
			view.setAlpha(1);
			dispatchRemoveFinished(item);
		}
		// 类似mPendingRemovals的清理
		if (mPendingAdditions.remove(item)) {
			view.setAlpha(1);
			dispatchAddFinished(item);
		}

		for (int i = mChangesList.size() - 1; i >= 0; i--) {
			ArrayList<ChangeInfo> changes = mChangesList.get(i);
			endChangeAnimation(changes, item);
			if (changes.isEmpty()) {
				mChangesList.remove(i);
			}
		}
		for (int i = mMovesList.size() - 1; i >= 0; i--) {
			ArrayList<MoveInfo> moves = mMovesList.get(i);
			for (int j = moves.size() - 1; j >= 0; j--) {
				MoveInfo moveInfo = moves.get(j);
				if (moveInfo.holder == item) {
					view.setTranslationY(0);
					view.setTranslationX(0);
					dispatchMoveFinished(item);
					moves.remove(j);
					if (moves.isEmpty()) {
						mMovesList.remove(i);
					}
					break;
				}
			}
		}
		for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
			ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
			if (additions.remove(item)) {
				view.setAlpha(1);
				dispatchAddFinished(item);
				if (additions.isEmpty()) {
					mAdditionsList.remove(i);
				}
			}
		}

		// animations should be ended by the cancel above.
		if (mRemoveAnimations.remove(item) && DEBUG) {
			throw new IllegalStateException("after animation is cancelled, item should not be in " + "mRemoveAnimations list");
		}

		if (mAddAnimations.remove(item) && DEBUG) {
			throw new IllegalStateException("after animation is cancelled, item should not be in " + "mAddAnimations list");
		}

		if (mChangeAnimations.remove(item) && DEBUG) {
			throw new IllegalStateException("after animation is cancelled, item should not be in " + "mChangeAnimations list");
		}

		if (mMoveAnimations.remove(item) && DEBUG) {
			throw new IllegalStateException("after animation is cancelled, item should not be in " + "mMoveAnimations list");
		}
		dispatchFinishedWhenDone();
	}

	private void resetAnimation(RecyclerView.ViewHolder holder) {
		if (sDefaultInterpolator == null) {
			sDefaultInterpolator = new ValueAnimator().getInterpolator();
		}
		holder.itemView.animate().setInterpolator(sDefaultInterpolator);
		endAnimation(holder);
	}

	@Override public boolean isRunning() {
		return (!mPendingAdditions.isEmpty() || !mPendingChanges.isEmpty() || !mPendingMoves.isEmpty() || !mPendingRemovals.isEmpty() || !mMoveAnimations.isEmpty() || !mRemoveAnimations.isEmpty()
				|| !mAddAnimations.isEmpty() || !mChangeAnimations.isEmpty() || !mMovesList.isEmpty() || !mAdditionsList.isEmpty() || !mChangesList.isEmpty());
	}

	/**
	 * Check the state of currently pending and running animations. If there are
	 * none pending/running, call {@link #dispatchAnimationsFinished()} to notify
	 * any listeners.
	 */
	private void dispatchFinishedWhenDone() {
		if (!isRunning()) {
			dispatchAnimationsFinished();
		}
	}

	@Override public void endAnimations() {
		int count = mPendingMoves.size();
		for (int i = count - 1; i >= 0; i--) {
			MoveInfo item = mPendingMoves.get(i);
			View view = item.holder.itemView;
			view.setTranslationY(0);
			view.setTranslationX(0);
			dispatchMoveFinished(item.holder);
			mPendingMoves.remove(i);
		}
		count = mPendingRemovals.size();
		for (int i = count - 1; i >= 0; i--) {
			RecyclerView.ViewHolder item = mPendingRemovals.get(i);
			dispatchRemoveFinished(item);
			mPendingRemovals.remove(i);
		}
		count = mPendingAdditions.size();
		for (int i = count - 1; i >= 0; i--) {
			RecyclerView.ViewHolder item = mPendingAdditions.get(i);
			item.itemView.setAlpha(1);
			dispatchAddFinished(item);
			mPendingAdditions.remove(i);
		}
		count = mPendingChanges.size();
		for (int i = count - 1; i >= 0; i--) {
			endChangeAnimationIfNecessary(mPendingChanges.get(i));
		}
		mPendingChanges.clear();
		if (!isRunning()) {
			return;
		}

		int listCount = mMovesList.size();
		for (int i = listCount - 1; i >= 0; i--) {
			ArrayList<MoveInfo> moves = mMovesList.get(i);
			count = moves.size();
			for (int j = count - 1; j >= 0; j--) {
				MoveInfo moveInfo = moves.get(j);
				RecyclerView.ViewHolder item = moveInfo.holder;
				View view = item.itemView;
				view.setTranslationY(0);
				view.setTranslationX(0);
				dispatchMoveFinished(moveInfo.holder);
				moves.remove(j);
				if (moves.isEmpty()) {
					mMovesList.remove(moves);
				}
			}
		}
		listCount = mAdditionsList.size();
		for (int i = listCount - 1; i >= 0; i--) {
			ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
			count = additions.size();
			for (int j = count - 1; j >= 0; j--) {
				RecyclerView.ViewHolder item = additions.get(j);
				View view = item.itemView;
				view.setAlpha(1);
				dispatchAddFinished(item);
				additions.remove(j);
				if (additions.isEmpty()) {
					mAdditionsList.remove(additions);
				}
			}
		}
		listCount = mChangesList.size();
		for (int i = listCount - 1; i >= 0; i--) {
			ArrayList<ChangeInfo> changes = mChangesList.get(i);
			count = changes.size();
			for (int j = count - 1; j >= 0; j--) {
				endChangeAnimationIfNecessary(changes.get(j));
				if (changes.isEmpty()) {
					mChangesList.remove(changes);
				}
			}
		}

		cancelAll(mRemoveAnimations);
		cancelAll(mMoveAnimations);
		cancelAll(mAddAnimations);
		cancelAll(mChangeAnimations);

		dispatchAnimationsFinished();
	}

	private void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
		for (int i = viewHolders.size() - 1; i >= 0; i--) {
			viewHolders.get(i).itemView.animate().cancel();
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * If the payload list is not empty, DefaultItemAnimator returns
	 * <code>true</code>. When this is the case:
	 * <ul>
	 * <li>If you override
	 * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
	 * both ViewHolder arguments will be the same instance.</li>
	 * <li>If you are not overriding
	 * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
	 * then DefaultItemAnimator will call
	 * {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and run a
	 * move animation instead.</li>
	 * </ul>
	 */
	@Override public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
		return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
	}
}